"""11B集中安装架测试台项目模块"""
import os
from colorsys import hsv_to_rgb

"""
模块功能：
    1. 囊括了11B的测试定义，规定了每一个显示的channel在屏幕上显示的额外信息
    2. 对已经经过JSON协议器解析后的数据再按照11B模块的数据进行解析对比，并以列表的形式返回错误的信息
    3. 在此模块中，将11B的协议解析函数封装成了一个类，方便调用
"""

from screen.LogControl.log_init import *
from screen.ClassRefactoring.object_refactoring import HoverInfoManager
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
from openpyxl.styles import PatternFill
from openpyxl.styles.colors import Color
from colorsys import hsv_to_rgb

"""11B集中安装架测试台项目模块的协议类"""


class Protocol_11B:
    def __init__(self, main_window):
        # 共享UI界面的对象
        self.main_window = main_window

        # 日志配置
        self.log = Log_Config()

        # 默认值
        self.__Default_value = '1' * 219

    """11B数据解包函数"""

    def protocol_11b_unpack(self, recv_data: bytes):
        """
        11BS协议解析函数
        :param recv_data:待解析的函数
        :return:状态码或者解析后的数据(数据只会将错误的数据返回)
        """

        # 判断功能ID
        if recv_data[0] != 0x01:
            self.log.logger.error("功能ID错误")
            return None

        # 获取有效数据长度
        length = recv_data[1]

        # 获取有效数据
        data = recv_data[2:length + 2]

        # 将数据转换为二进制数据
        #  {0:08b} 是一个格式化字符串，其中0是占位符，表示我们要插入的值（这里是byte）
        # :08b是格式规范，表示输出一个二进制表示，总宽度至少为8位，不够前面补0。
        binary_str_parts = ['{0:08b}'.format(byte) for byte in data]

        # 将数据转换为字符串
        binary_str = ''.join(binary_str_parts)

        return self.__protocol_11b_compare(binary_str)

    def __protocol_11b_compare(self, recv_data: str):
        """
        11BS协议数据对比函数， 比较两个长度相同的二进制数据，记录不匹配的位索引。
        :param recv_data:待比较的未知数据，二进制形式，长度也为219位。
        :return:不匹配位的索引列表。
        """
        try:
            # 判断长度
            assert (len(recv_data) - 5) == len(self.__Default_value), "11BS模块对比的数据长度不一致"
            assert len(recv_data) >= 219, "11B模块对比的数据长度不足219位"

            # 按位对比并返回不同的索引值
            # zip函数是将两个字符串配对，形成一个迭代器。迭代器中的每一个元素都是一对字符，分别对应两个字符串中的相同位置的字符，比如(r1,u1),(r2,u2),(r3,u3)
            # enumerate函数将迭代器中的每一个元素转化为一个元组，元组的第一个元素是索引，第二个元素是迭代器中的元素。比如(0,(r1,u1)),(1,(r2,u2)),(2,(r3,u3))
            mismatch_indices = [i + 1 for i, (rd, ud) in enumerate(zip(self.__Default_value, recv_data)) if rd != ud]

            return mismatch_indices

        except AssertionError as e:
            self.log.logger.error(e)
            return None

    def _11b_initialize_hover_info(self):
        """
        在UI初始化完成后设置悬停信息
        """
        self.hoverManager = HoverInfoManager()

        # 循环绑定通道中文信息
        for i in range(1, 220):
            index = min((i - 1) // 128 + 1, 3)  # i从1开始，因此减1再除以128，加1得到index，同时限制最大为3
            second_index = 128 if i in (128, 256) else i % 128
            channel_name = f'channel_{index}_{second_index}'
            channel_widget = getattr(self.main_window, channel_name, None)
            if channel_widget:
                self.hoverManager.add_hover_info(channel_widget, self.__get_channel_name(i))

    """获取通道中文名称"""

    def __get_channel_name(self, channel_number: int) -> str:
        """
        获取通道中文名称
        :param channel_number:通道编号
        :return:通道中文名称
        """
        # 219个通道元素：参考11B模块的通道测试定义
        self.Chinese = {1: "地勤话筒+", 2: "地勤话筒-", 3: "屏蔽地", 4: "短波电台收入+", 5: "短波电台收入-",
                        6: "屏蔽地", 7: "塔康导航音+", 8: "塔康导航音-", 9: "屏蔽地", 10: "MLS导航音+",
                        11: "MLS导航音-",
                        12: "屏蔽地", 13: "JIDS2 收+", 14: "JIDS2 收-",
                        15: "屏蔽地", 16: "导弹告警+", 17: "导弹告警-", 18: "屏蔽地", 19: "地勤耳机+",
                        20: "地勤耳机-", 21: "屏蔽地",
                        22: "短波电台发+", 23: "短波电台发-", 24: "屏蔽地", 25: "话台收明话+", 26: "话台收明话-",
                        27: "屏蔽地", 28: "预留",
                        29: "预留", 30: "屏蔽地", 31: "话台救生明话+", 32: "话台救生明话-", 33: "屏蔽地",
                        34: "话台发明话+", 35: "话台发明话-",
                        36: "屏蔽地", 37: "预留", 38: "预留", 39: "屏蔽地", 40: "预留",
                        41: "预留", 42: "屏蔽地",
                        43: "预留", 44: "预留", 45: "屏蔽地", 46: "预留", 47: "预留",
                        48: "屏蔽地",
                        49: "预留", 50: "预留", 51: "屏蔽地", 52: "罗盘导航音+", 53: "罗盘导航音-",
                        54: "屏蔽地", 55: "信标机音+",
                        56: "信标机音-", 57: "屏蔽地", 58: "去显控盒耳机+", 59: "去显控盒耳机-", 60: "屏蔽地",
                        61: "去VTR记录音+",
                        62: "去VTR记录音-",
                        63: "屏蔽地", 64: "串口2收+", 65: "串口2收-", 66: "屏蔽地", 67: "串口2发+", 68: "串口2发-",
                        69: "屏蔽地",
                        70: "启动信标机自检", 71: "信标过台信号", 72: "CCU PTT", 73: "BPU PPT",
                        74: "话台PTT", 75: "预留", 76: "预留",
                        77: "预留", 78: "灯测试信号", 79: "灯测试信号", 80: "CCU故障",
                        81: "BPU故障", 82: "产生告警音10", 83: "产生告警音11",
                        84: "产生告警音14", 85: "产生告警音15", 86: "产生告警音16", 87: "产生告警音17",
                        88: "产生告警音18", 89: "产生告警音19", 90: "产生告警音20",
                        91: "产生告警音23", 92: "产生告警音24", 93: "产生告警音24", 94: "产生告警音25",
                        95: "产生告警音26",
                        96: "产生告警音27", 97: "产生告警音28",
                        98: "产生告警音31", 99: "产生告警音32", 100: "产生告警音33", 101: "产生告警音34",
                        102: "产生告警音35", 103: "产生告警音36",
                        104: "产生告警音37", 105: "产生告警音38", 106: "产生告警音39", 107: "供电指示",
                        108: "JIDS1 收+", 109: "JIDS1 收-",
                        110: "屏蔽地", 111: "JIDS2发十", 112: "JIDS2 发-", 113: "屏蔽地",
                        114: "数台毁钥控制", 115: "数台毁钥控制",
                        116: "数台毁钥回线", 117: "产生告警音1", 118: "产生告警音2", 119: "产生告警音3",
                        120: "产生告警音4",
                        121: "产生告警音5",
                        122: "产生告警音7", 123: "产生告警音12", 124: "产生告警音13", 125: "125	产生告警音8",
                        126: "产生告警音21", 127: "产生告警音22",
                        128: "产生告警音29", 129: "产生告警音30", 130: "产生告警音6", 131: "产生告警音8",
                        132: "BPU来收明话+", 133: "BPU来收明话-",
                        134: "屏蔽地", 135: "罗盘微波着陆+", 136: "罗盘微波着陆-", 137: "屏蔽地",
                        138: "信标机+", 139: "信标机-",
                        140: "屏蔽地", 141: "去BPU主机耳机+", 142: "去BPU主机耳机-", 143: "屏蔽地",
                        144: "去BPU发明话+", 145: "去BPU发明话-",
                        146: "屏蔽地", 147: "去 BPU 的 VTR+", 148: "去 BPU 的 VTR-", 149: "屏蔽地", 150: "串行口3收+",
                        151: "串行口3收-",
                        152: "屏蔽地", 153: "串行口3发+", 154: "串行口3发-", 155: "屏蔽地",
                        156: "睡眠指示+", 157: "睡眠指示-",
                        158: "屏蔽地", 159: "话台PPT出", 160: "数台PPT出", 161: "数台话台指示",
                        162: "加电控制", 163: "加电控制回线",
                        164: "28V(+)", 165: "28V(+)", 166: "28V(+)", 167: "机载28V地",
                        168: "机载28V地", 169: "机载28V地",
                        170: "+12V", 171: "CCU地", 172: "收音频(+)", 173: "发音频㈠", 174: "屏蔽地",
                        175: "发音频⑴",
                        176: "发音频㈠", 177: "屏蔽地", 178: "加电控制回线", 179: "加电控制",
                        180: "机载+28V", 181: "机载+28V",
                        182: "机载+28V", 183: "机载+28V回线", 184: "机载+28V回线", 185: "机载+28V回线",
                        186: "去显控盒+12V", 187: "去显控盒BPU地",
                        188: "产生告警音1", 189: "产生告警音2", 190: "产生告警音3", 191: "产生告警音4",
                        192: "产生告警音9", 193: "电源模块电源控制",
                        194: "地", 195: "数台加电控制", 196: "数台加电闽线", 197: "产生告瞀音5",
                        198: "产生告蝥音7", 199: "产生告警音12 ",
                        200: "产生告鳘音13", 201: "产生告警音8", 202: "产生告警音21", 203: "产生告警音22",
                        204: "产生告警音29",
                        205: "产生告警音30",
                        206: "产生告警音6", 207: "产生告警音8", 208: "27V+",
                        209: "27V+", 210: "27V+", 211: "27V+",
                        212: "27V地", 213: "27V地", 214: "27V地", 215: "27V地", 216: "三相交流电",
                        217: "三相交流电",
                        218: "三相交流电", 219: "接地"}
        # 遍历字典的键并返回值
        if channel_number in self.Chinese:
            return self.Chinese[channel_number]
        else:
            self.log.logger.error("11B模块通道编号错误")
            return " "

    def __generate_color_map(self, n):
        """
        生成n个不同的颜色，用于标记不同的错误通道号。
        :param n: 需要的颜色数量
        :return: 包含PatternFill对象的颜色映射字典
        """
        colors = {}
        step = 1.0 / n
        for i in range(n):
            hue = i * step
            rgb = hsv_to_rgb(hue, 1.0, 1.0)
            color = Color(rgb=f"{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}")
            fill = PatternFill(start_color=color.rgb, end_color=color.rgb, fill_type='solid')
            colors[i] = fill
        return colors

    """错误信息生成文档"""

    def _11b_erro_info_save_excel(self, info: dict):
        """
        将错误列表的信息定位到文档中，并生成一个新的错误文档。
        :param info:错误列表
        :return:None
        """
        try:
            # 文件路径
            file_path = 'Doc/StandardForm/11B测试定义查询表格.xlsx'
            # 输出文件路径
            output_path = 'Doc/ErroForm/11B测试结果错误表格.xlsx'

            # 加载Excel工作簿
            wb = load_workbook(file_path)
            sheet = wb.active

            # 匹配列名
            column_to_match = "序号"

            # 错误字典
            values_to_style = info

            # 获取颜色字典
            color_map = self.__generate_color_map(len(values_to_style))

            # 默认填充样式
            default_fill = PatternFill(fill_type=None)

            # 获取“序号”列的索引
            header_row = next(sheet.iter_rows(min_row=2, max_row=3, values_only=True))
            try:
                column_index = header_row.index(column_to_match) + 1  # 索引从1开始
            except ValueError:
                self.log.logger.error(f"列名'{column_to_match}'未找到，请检查列名是否正确。")
                return None

            # 创建一个字典，用于存储“序号”到行号的映射
            number_to_row = {row[column_index - 1]: idx for idx, row in
                             enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2)}

            # 清除所有单元格的现有填充样式，以避免样式叠加
            for row in sheet.iter_rows(min_row=2, max_col=sheet.max_column):
                for cell in row:
                    cell.fill = default_fill

            # 遍历错误字典中的每个键值对
            for i, (main_channel, connected_channels) in enumerate(values_to_style.items()):
                # 确定color_map中的键
                map_key = i % len(color_map)
                # 应用样式到主通道的行
                if main_channel in number_to_row:
                    row_num = number_to_row[main_channel]
                    # 更改B和C列的单元格样式
                    for col in sheet.iter_cols(min_col=2, max_col=3, min_row=row_num, max_row=row_num):
                        for cell in col:
                            cell.fill = color_map[map_key]

                # 应用样式到与主通道错误连接的其他通道的行
                for connected_channel in connected_channels:
                    if connected_channel in number_to_row:
                        row_num = number_to_row[connected_channel]
                        # 更改D和E列的单元格样式
                        for col in sheet.iter_cols(min_col=4, max_col=5, min_row=row_num, max_row=row_num):
                            for cell in col:
                                cell.fill = color_map[map_key]

            # 保存更改到原文件
            wb.save(output_path)
            wb.close()
            return True

        except Exception as e:
            self.log.logger.error(f"错误信息生成文档失败:{e}")
            return False
